package org.summerframework.component.enums.config;

import org.summerframework.component.enums.annotation.EnumGroup;
import org.summerframework.component.enums.annotation.EnumKind;
import org.summerframework.component.enums.service.EnumsService;
import org.summerframework.component.enums.service.dto.EnumsDTO;
import org.summerframework.component.enums.service.dto.EnumsQueryDTO;
import org.summerframework.component.mybites.config.MybatisConfiguration;
import org.summerframework.component.mybites.config.TransactionManagementConfiguration;
import org.summerframework.core.annotation.FilterClassPathBeanDefinitionScanner;
import org.summerframework.core.base.dto.BaseEnum;
import org.summerframework.core.util.AnnotationUtils;
import org.summerframework.core.util.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author 石超
 * @version v1.0.0
 */
@Configuration
@ComponentScan({
        "org.summerframework.component.enums.service",
        "org.summerframework.component.enums.controller"
})
@MapperScan(value = "org.summerframework.component.enums.model",
        annotationClass = Repository.class)
@AutoConfigureAfter({MybatisConfiguration.class, TransactionManagementConfiguration.class})
public class EnumsAutoConfiguration {

    @Slf4j
    @Configuration
    @ConditionalOnProperty(name = "application.enums", havingValue = "true")
    public static class EnumsInitConfiguration implements ApplicationListener<ApplicationStartedEvent> {

        private static final ConfigurationPropertyName CLIENT_LOG = ConfigurationPropertyName.of("enums");

        private static final Bindable<List<String>> STRING_CLIENT_LOG_PROPERTY_MAP = Bindable.listOf(String.class);

        @Resource
        private SpringContextUtil springContextUtil;

        @Override
        public void onApplicationEvent(ApplicationStartedEvent event) {
            List<Object> list = new ArrayList<>();
            Binder binder = Binder.get(event.getApplicationContext().getEnvironment());
            List<String> packageNames = binder.bind(CLIENT_LOG, STRING_CLIENT_LOG_PROPERTY_MAP).orElseGet(Collections::emptyList);

            //system basePackage
            packageNames.add("org.summerframework");

            EnumsService enumsService = SpringContextUtil.getBean(EnumsService.class);
            GenericApplicationContext context = new GenericApplicationContext();
            FilterClassPathBeanDefinitionScanner myClassPathBeanDefinitionScanner = new FilterClassPathBeanDefinitionScanner(context, new AnnotationTypeFilter(EnumKind.class));

            packageNames.forEach(packageName -> {
                try {
                    //根据Order排序
                    List<BeanDefinition> candidateComponents = new ArrayList<>(myClassPathBeanDefinitionScanner.findCandidateComponents(packageName));
                    AnnotationAwareOrderComparator.sort(candidateComponents);

                    for (BeanDefinition beanDefinition : candidateComponents) {
                        Class<?> aClass = Class.forName(beanDefinition.getBeanClassName());

                        //跳过加载过的class
                        if (list.contains(aClass)) {
                            return;
                        }

                        list.add(aClass);
                        if (Enum.class.isAssignableFrom(aClass) && BaseEnum.class.isAssignableFrom(aClass)) {
                            EnumKind kind = AnnotationUtils.findAnnotation(aClass, EnumKind.class);

                            //跳过加载过的kind分组
                            if (list.contains(kind.kind())) {
                                return;
                            }

                            list.add(kind.kind());
                            BaseEnum[] values = (BaseEnum[]) aClass.getMethod("values").invoke(aClass);
                            List<EnumsDTO> enumsDTOS = enumsService.selectByQuery(EnumsQueryDTO.builder().kind(kind.kind()).isPagination(false).build());

                            AtomicInteger count = new AtomicInteger();
                            for (BaseEnum baseEnum : values) {
                                EnumGroup group = AnnotationUtils.findAnnotation(baseEnum.getClass(), EnumGroup.class);

                                EnumsDTO enumsDTO = new EnumsDTO();
                                enumsDTO.setGroup(kind.group());
                                enumsDTO.setKind(kind.kind());
                                enumsDTO.setSortIndex(count.incrementAndGet());
                                enumsDTO.setIsVisible(kind.isVisible() ? 1 : 0);
                                enumsDTO.setCode(baseEnum.getCode());
                                enumsDTO.setDesc(baseEnum.getDesc());

                                if (group != null) {
                                    if (group.sortIndex() > 0) {
                                        enumsDTO.setSortIndex(group.sortIndex());
                                    }

                                    enumsDTO.setGroup(group.group());
                                    enumsDTO.setIsVisible(group.isVisible() ? 1 : 0);
                                    enumsDTO.setRemarks(group.remarks());
                                }

                                Optional<EnumsDTO> first = enumsDTOS.stream().filter(enumsDTO1 -> enumsDTO1.getCode().equals(baseEnum.getCode())).findFirst();

                                if (first.isPresent()) {
                                    EnumsDTO enums = first.get();
                                    enumsDTO.setId(enums.getId());
                                    enumsService.updateById(enumsDTO);
                                    enumsDTOS.remove(enums);
                                } else {
                                    enumsService.insert(enumsDTO);
                                }
                            }

                            enumsService.deleteByIds(enumsDTOS.stream().map(EnumsDTO::getId).collect(Collectors.toList()));
                        } else {
                            log.warn("{} not extent java.lang.Enum and implements org.summerframework.core.base.dto.BaseEnum", aClass.getName());
                        }
                    }
                } catch (Exception e) {
                    log.error("初始化枚举类失败", e);
                }
            });
        }
    }
}
